#include "scps.h"
#include "scpstp.h"
#include "scpserrno.h"
#include "tp_debug.h"
#include "gw.h"
#include <stdio.h>
#include <math.h>
#include "other_proto_handler.h"
#include "rt_alloc.h"
#include "tap.h"
#include "scps_ip.h"
#include "scps_np.h"
#include "scps_defines.h"
#include "rs_config.h"

extern GW_ifs gw_ifs;
extern route *def_route;
extern route *other_route;


extern tp_Socket *tp_allsocs;  /* Pointer to first TP socket */
extern uint32_t tp_now;
int delayed_sent;
extern short tp_id;
extern int cluster_check;
struct llbuf *in_data;      /* packet buffer */
extern GW_ifs gw_ifs;

other_proto_q_t other_proto [PROTO_MAX] [2];
other_proto_q_t other_proto_non_ip [2];
other_proto_q_t other_proto_ipv6 [2];


// Handler for incoming TP packets.
void other_proto_init (void)
{
  int i, j;

  for (i = 0; i < PROTO_MAX; i++) {
    for (j = 0; j < 2; j++) {
      other_proto [i] [j]. q = q_create ();
      other_proto [i] [j]. def_rt = NULL;
      other_proto [i] [j]. rt = NULL;
    }
  }

  for (j = 0; j < 2; j++) {
    other_proto_non_ip [j]. q = q_create ();
    other_proto_non_ip [j]. def_rt = NULL;
    other_proto_non_ip [j]. rt = NULL;
  }
  for (j = 0; j < 2; j++) {
    other_proto_ipv6 [j]. q = q_create ();
    other_proto_ipv6 [j]. def_rt = NULL;
    other_proto_ipv6 [j]. rt = NULL;
  }
}

void other_proto_non_ip_Handler (int interface_side, struct llbuf **buffer, 
    scps_np_rqts *rqts, int fd)
{
  other_proto_pkt_t *pkt;

  if (other_proto_non_ip [interface_side].q->q_len <= gw_ifs.c_other_proto_qlen) {
    if (interface_side == BIF) {
      other_proto_non_ip [interface_side].rt = other_route;
      other_proto_non_ip [interface_side].def_rt = other_route;
    } else {
      other_proto_non_ip [interface_side].rt = def_route;
      other_proto_non_ip [interface_side].def_rt = def_route;
    }

    pkt =  (other_proto_pkt_t *) malloc (sizeof (other_proto_pkt_t));
    pkt->fd = fd;
    memcpy (&(pkt->data [0]), (*buffer)->data, (*buffer)->size);
    pkt->length = (*buffer)->size;
    pkt->offset = (*buffer)->offset;
    q_addt (other_proto_non_ip [interface_side].q, (char *) pkt);
  }
        free_llbuf (rqts->gwio, *buffer);

}

void other_proto_ipv6_Handler (int interface_side, struct llbuf **buffer, 
      scps_np_rqts *rqts, int fd)
{
  other_proto_pkt_t *pkt;

  if (other_proto_ipv6 [interface_side].q->q_len <= gw_ifs.c_other_proto_qlen) {
      if (interface_side == BIF) {
              other_proto_ipv6 [interface_side].rt = other_route;
              other_proto_ipv6 [interface_side].def_rt = other_route;
      } else {
              other_proto_ipv6 [interface_side].rt = def_route;
              other_proto_ipv6 [interface_side].def_rt = def_route;
      }

      pkt =  (other_proto_pkt_t *) malloc (sizeof (other_proto_pkt_t));
      pkt->fd = fd;
      memcpy (&(pkt->data [0]), (*buffer)->data, (*buffer)->size);
      pkt->length = (*buffer)->size;
      pkt->offset = (*buffer)->offset;
      q_addt (other_proto_ipv6 [interface_side].q, (char *) pkt);
  }
  free_llbuf (rqts->gwio, *buffer);

}


void other_proto_Handler (struct gwio *gwio, struct llbuf **buffer, 
  int max_len, int *offset, scps_np_rqts *rqts, unsigned char proto)
{
  other_proto_pkt_t *pkt;
  int interface_side = -1;
  int fd;


  if (rqts->divert_port_number == gw_ifs.a.divport) {
  interface_side = BIF;
  fd = gwio->tap_b_fd;
        other_proto [proto] [interface_side].rt = other_route;
        other_proto [proto] [interface_side].def_rt = other_route;
  } else {
  interface_side = AIF;
  fd = gwio->tap_a_fd;
        other_proto [proto] [interface_side].rt = def_route;
        other_proto [proto] [interface_side].def_rt = def_route;
  }

  if (other_proto [proto] [interface_side].q->q_len <=
      gw_ifs.c_other_proto_qlen) {

      pkt =  (other_proto_pkt_t *) malloc (sizeof (other_proto_pkt_t));
      pkt->fd = fd;
      memcpy (&(pkt->src_mac_addr [0]), &(rqts->src_mac_addr [0]),6);
      memcpy (&(pkt->dst_mac_addr [0]), &(rqts->dst_mac_addr [0]),6);
      pkt->frame_type = rqts->frame_type;
      memcpy (&(pkt->data [0]), (*buffer)->data, MAX_LL_DATA);
      pkt->length = (*buffer)->size;
      pkt->offset = (*buffer)->offset;
      q_addt (other_proto [proto] [interface_side].q, (char *) pkt); 
  }

  free_llbuf (rqts->gwio, *buffer);
}


void other_proto_emit ()
{
  int i, j;
    other_proto_pkt_t *pkt;
        int rc;
  int len;

  for (i = 0; i < PROTO_MAX; i++) {
    for (j = 0; j < 2; j++) {
      if (q_empty (other_proto [i] [j].q)) {

      } else {
        if (( ((other_proto_pkt_t *)
               other_proto [i] [j].q->q_head->qe_data)->length <
               other_proto [i] [j].rt->current_credit) 
                  ) {  
          pkt = (other_proto_pkt_t *) q_deq (other_proto [i] [j].q);
        
          len = pkt->length;

          pkt->offset -= ETH_HLEN;
          pkt->length += ETH_HLEN;

          rc = ll_tap_qk_send (pkt->fd, 
                                                pkt->data + pkt->offset,
                  pkt->length);
          if (rc > 0) {
                   other_proto [i] [j].rt->current_credit -= len;
          }
          free (pkt);
        } else if (gw_ifs.c_other_proto_xrate_drop == 1) {
          pkt = (other_proto_pkt_t *) q_deq (other_proto [i] [j].q);
          free (pkt);
        } else {

        }
      }
    }
  }
}



void other_proto_non_ip_emit ()
{
  int j;
    other_proto_pkt_t *pkt;
        int rc;

  for (j = 0; j < 2; j++) {
    if (q_empty (other_proto_non_ip [j].q)) {

    } else {
      if ( ( ((other_proto_pkt_t *)
             other_proto_non_ip [j].q->q_head->qe_data)->length <
             other_proto_non_ip [j].rt->current_credit) 
                  ) {  
        pkt = (other_proto_pkt_t *) q_deq (other_proto_non_ip [j].q);
  
        rc = ll_tap_qk_send (pkt->fd, 
                                                pkt->data + pkt->offset,
                pkt->length);

        if (rc > 0) {
                 other_proto_non_ip [j].rt->current_credit -= rc;
        }

        free (pkt);
      } else if (gw_ifs.c_other_proto_xrate_drop == 1) {
        pkt = (other_proto_pkt_t *) q_deq (other_proto_non_ip [j].q);
        free (pkt);
      } else {
  
      }
    }
  }
}

void other_proto_ipv6_emit ()

{
        int j;
        other_proto_pkt_t *pkt;
        int rc;

        for (j = 0; j < 2; j++) {
                if (q_empty (other_proto_ipv6 [j].q)) {

                } else {
                        if ( ( ((other_proto_pkt_t *)
                               other_proto_ipv6 [j].q->q_head->qe_data)->length <
                               other_proto_ipv6 [j].rt->current_credit)
        ) {
                                pkt = (other_proto_pkt_t *) q_deq (other_proto_ipv6 [j].q);

                                rc = ll_tap_qk_send (pkt->fd,
                                               pkt->data + pkt->offset,
                                        pkt->length);

        if (rc > 0) {
                                  other_proto_ipv6 [j].rt->current_credit -= rc;
        }
                                free (pkt);
                        } else if (gw_ifs.c_other_proto_xrate_drop == 1) {
                                pkt = (other_proto_pkt_t *) q_deq (other_proto_ipv6 [j].q);
                                free (pkt);
                        } else {

                        }
                }
        }
}


